package org.unidata.mdm.rest.v1.data.service.atomic;

import javax.ws.rs.HttpMethod;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.unidata.mdm.data.context.GetRequestContext;
import org.unidata.mdm.data.context.UpsertRequestContext;
import org.unidata.mdm.data.dto.GetRecordDTO;
import org.unidata.mdm.data.dto.UpsertRecordDTO;
import org.unidata.mdm.rest.system.ro.CompositeInputRO;
import org.unidata.mdm.rest.system.ro.DetailedCompositeOutputRO;
import org.unidata.mdm.rest.system.ro.DetailedErrorResponseRO;
import org.unidata.mdm.rest.v1.data.ro.atomic.AtomicDataGetRequestRO;
import org.unidata.mdm.rest.v1.data.ro.atomic.AtomicDataGetResultRO;
import org.unidata.mdm.rest.v1.data.ro.atomic.AtomicDataUpsertRequestRO;
import org.unidata.mdm.rest.v1.data.ro.atomic.AtomicDataUpsertResultRO;
import org.unidata.mdm.rest.v1.data.service.AbstractDataRestService;
import org.unidata.mdm.system.context.InputCollector;
import org.unidata.mdm.system.dto.OutputContainer;
import org.unidata.mdm.system.type.runtime.MeasurementContextName;
import org.unidata.mdm.system.type.runtime.MeasurementPoint;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse;

/**
 * @author Alexandr Serov
 * @since 09.11.2020
 **/
@Path("atomic")
public class DataAtomicRestService extends AbstractDataRestService {

    private static final String ATOMIC_TAG = "atomic";

    @POST
    @Path("/")
    @Produces({MediaType.APPLICATION_JSON})
    @Operation(
        description = "Atomic upsert data",
        method = HttpMethod.POST,
        requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = AtomicDataUpsertRequestRO.class)), description = "Atomic upsert request"),
        responses = {
            @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "400"),
            @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "500"),
            @ApiResponse(content = @Content(schema = @Schema(implementation = AtomicDataUpsertResultRO.class)), responseCode = "200")
        }, tags = ATOMIC_TAG)
    public AtomicDataUpsertResultRO upsert(AtomicDataUpsertRequestRO req) {

        MeasurementPoint.init(MeasurementContextName.MEASURE_UI_UPDATE);
        MeasurementPoint.start();
        try {

            UpsertRequestContext ctx = renderInputAndGet(req, UpsertRequestContext.builder()).build();
            UpsertRecordDTO executeRequest = dataRecordsService.upsertRecord(ctx);

            return renderOutputAndGet(executeRequest, new AtomicDataUpsertResultRO());
        } finally {
            MeasurementPoint.stop();
        }
    }

    @POST
    @Path("/get")
    @Produces({MediaType.APPLICATION_JSON})
    @Operation(
        description = "Atomic GET data",
        method = HttpMethod.POST,
        requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = AtomicDataGetRequestRO.class)), description = "Atomic GET request"),
        responses = {
            @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "400"),
            @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "500"),
            @ApiResponse(content = @Content(schema = @Schema(implementation = AtomicDataGetResultRO.class)), responseCode = "200")
        }, tags = ATOMIC_TAG)
    public AtomicDataGetResultRO get(AtomicDataGetRequestRO req) {

        MeasurementPoint.init(MeasurementContextName.MEASURE_UI_GET);
        MeasurementPoint.start();
        try {

            GetRequestContext ctx = renderInputAndGet(req, GetRequestContext.builder()).build();
            GetRecordDTO executeRequest = dataRecordsService.getRecord(ctx);

            return renderOutputAndGet(executeRequest, new AtomicDataGetResultRO());
        } finally {
            MeasurementPoint.stop();
        }
    }

    protected <T extends InputCollector> T renderInputAndGet(CompositeInputRO input, T context) {
        renderingService.renderInput(input.getInputRenderingAction(), context, input);
        return context;
    }

    protected <T extends DetailedCompositeOutputRO> T renderOutputAndGet(OutputContainer container, T output) {
        renderingService.renderOutput(output.getOutputRenderingAction(), container, output);
        return output;
    }
}
